/*
 * FreeRTOS V202104.00
 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * https://www.FreeRTOS.org
 * https://aws.amazon.com/freertos
 *
 */

/**
 * @file agent_message_processor.c
 * @brief Implements functions to obtain and release commands.
 */

/* Standard includes. */
#include <assert.h>
#include <stdio.h>
#include <string.h>

#include "cmsis_os2.h"

/* Header include. */
#include "agent_message_processor.h"
#include "core_mqtt_config.h"
#include "message_handler.h"

/*-----------------------------------------------------------*/

typedef enum { QUEUE_STATE_UNINITIALIZED = 0, QUEUE_STATE_INITIALIZED } QueueState_t;

/**
 * @brief The pool of command structures used to hold information on commands (such
 * as PUBLISH or SUBSCRIBE) between the command being created by an API call and
 * completion of the command by the execution of the command's callback.
 */
static MQTTAgentCommand_t commandStructurePool[MQTT_COMMAND_CONTEXTS_POOL_SIZE];

/**
 * @brief The message context used to guard the pool of MQTTAgentCommand_t structures.
 * This is implemented with a queue. Structures may be obtained by receiving a
 * pointer from the queue, and returned by sending the pointer back into it.
 */
static MQTTAgentMessageContext_t commandStructMessageCtx;

/**
 * @brief Initialization status of the queue.
 */
static volatile QueueState_t queueState = QUEUE_STATE_UNINITIALIZED;

void Agent_InitializePool(void)
{
    if (queueState == QUEUE_STATE_INITIALIZED) {
        return;
    }

    memset((void *)commandStructurePool, 0x00, sizeof(commandStructurePool));

    commandStructMessageCtx.queue =
        osMessageQueueNew(MQTT_COMMAND_CONTEXTS_POOL_SIZE, sizeof(MQTTAgentCommand_t *), NULL);
    assert((uint32_t)commandStructMessageCtx.queue);

    /* Populate the queue. */
    for (size_t i = 0; i < MQTT_COMMAND_CONTEXTS_POOL_SIZE; i++) {
        MQTTAgentCommand_t *pCommand = &commandStructurePool[i];
        assert(Agent_MessageSend(&commandStructMessageCtx, &pCommand, 0U));
    }

    queueState = QUEUE_STATE_INITIALIZED;
}

/*-----------------------------------------------------------*/

MQTTAgentCommand_t *Agent_GetCommand(uint32_t blockTimeMs)
{
    assert(queueState == QUEUE_STATE_INITIALIZED);

    MQTTAgentCommand_t *pCommand = NULL;
    if (!Agent_MessageReceive(&commandStructMessageCtx, &pCommand, blockTimeMs)) {
        LogError(("No command structure available."));
    }

    return pCommand;
}

/*-----------------------------------------------------------*/

bool Agent_ReleaseCommand(MQTTAgentCommand_t *pCommandToRelease)
{
    bool wasCmdReleased = false;

    assert(queueState == QUEUE_STATE_INITIALIZED);

    /* See if the structure being returned is actually from the pool. */
    if ((pCommandToRelease >= commandStructurePool)
        && (pCommandToRelease < (commandStructurePool + MQTT_COMMAND_CONTEXTS_POOL_SIZE))) {
        wasCmdReleased = Agent_MessageSend(&commandStructMessageCtx, &pCommandToRelease, 0U);

        /* The send should not fail as the queue was created to hold every command
         * in the pool. */
        assert(wasCmdReleased);

        LogDebug(("Returned Command Context %d to pool", (int)(pCommandToRelease - commandStructurePool)));
    }

    return wasCmdReleased;
}
